﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Threading;
using ExcelDna.Integration;
using ExcelDna.Integration.Rtd;
using FastLogAsync;

namespace ExcelDnaDemo
{
    public static class RTDFunctions
    {
        [ExcelFunction(Description = "RTD测试。使用方法：在单元格输入公式 =RTDTimer(\"当前时间：\")",
                   Category = "ExcelDNADemo for WPS个人版",
                   IsHidden = false, IsVolatile = true, IsMacroType = true,
                   Name = "RTDTimer")]
        public static object GetTime([ExcelArgument(Description = "标识文本", Name = "param1")] object Param1)
        {
            Log.Info("run udf GetTime");
            //ET在弹出函数向导之前，就调用了UDF。导致无法获取bosa_sdm_XL9。得到的窗口类名是Qt5QWindowToolSaveBits
            //点确定关闭函数向导之后，窗口虽然看不到了，但是IsInFunctionWizard遍历窗口还能找到bosa_sdm_XL9。？？
            if (ExcelDnaUtil.IsInFunctionWizard())
            {
                Log.Info("IsInFunctionWizard=true");
                if (ExcelDnaUtil.IsET)
                    return "请按F9初始化!";
                else
                    return "等待关闭函数向导窗口...";
            }
            else
                Log.Info("IsInFunctionWizard=false");
            if (Param1 is ExcelMissing) return "需要输入参数";                      //ExcelError.ExcelErrorValue;

            string[] param = new string[] { Param1.ToString()};
            string rtdservername = MethodBase.GetCurrentMethod().DeclaringType.Namespace + ".RTDTimerServer";
            
            return XlCall.RTD(rtdservername, null, param);                        //RTD服务器的名字就是namespace
        }
    }
    class RTDTimerServer : ExcelRtdServer
    {
        bool isbusy = false;
        /// <summary>
        /// 需要保留的RTD Topics信息
        /// </summary>
        private class RealRTDData      //内嵌类可以定义成private
        {
            public Topic Topic { get; set; }
            public IList<string> Params { get; set; }
        }
        /// <summary>
        /// 字典用于储存所有的Topics信息
        /// </summary>
        private readonly Dictionary<string, RealRTDData> topics = new Dictionary<string, RealRTDData>();

        private Timer timer;

        public RTDTimerServer()
        {
            timer = new Timer(Callback);
            Start();
        }

        private void Start()
        {
            Log.Info("RTD timer start");
            if (timer == null)
            {
                Log.Info("RTD timer start new Timer(Callback)");
                timer = new Timer(Callback);
            }
            timer.Change(50, 5000);
        }
        
        private void Stop()
        {
            Log.Info("RTD timer stop");
            if (timer != null)
                timer.Change(-1, -1);
            else
                Log.Info("RTD timer stop timer is null");
        }
        /// <summary>
        /// 定时器回调函数，定时抓取和讯网实时行情，并更新RTD数据
        /// </summary>
        /// <param name="o"></param>
        private void Callback(object o) 
        {
            if (isbusy) return;
            Log.Info("RTD timer callback");
            isbusy = true;
            try
            {
                //刷新全部的Topics，更新数据
                foreach (KeyValuePair<string, RealRTDData> kvp in topics)
                {
                    string word = kvp.Value.Params.First();
                    kvp.Value.Topic.UpdateValue(word + DateTime.Now.ToLongTimeString());
                    
                }
            }
            finally
            {
                isbusy = false;
            }
        }


        protected override void ServerTerminate()
        {
            Log.Info("RTD ServerTerminate");
            timer.Dispose();
            timer = null;
        }

        //该方法是在当打开一个含有RTD函数的文档，或者当用户往单元格中输入一个RTD函数之后就会执行。
        //该方法第一个参数为TopicID，改ID唯一标识了改单元格内的RTD函数，他是Excel自动分配的。
        //在有些情况下，我们需要定义好自己的数据结构，记录下TopicID，然后在后面可以刷新指定的TopicID的单元格。
        //第二个参数为Array类型的String对象，一般的利用该参数传递RTD中所需要的参数信息，以数组的形式传进来。
        //第三个参数为Bool型的GetNewValues，它用于确定是否总是获取最新数据，
        //如果这个参数传入true，则每次保存Excel文档以后，再次重新打开时，会计算是最新的实时数据，这也是一个引用类型的参数。
        //通常如果不设置的话，再次打开包含RTD的表格时，显示的是最后一次请求的数据。
        /// <summary>
        /// RTD连接，记录topics信息到字典里面
        /// </summary>
        protected override object ConnectData(Topic topic, IList<string> topicInfo, ref bool newValues)
        {
            Start();  //初始化定时器
            Log.Info($"RTD ConnectData {topic.TopicId},{topicInfo.First()}");
            topics.Add(topic.TopicId.ToString(), new RealRTDData { Topic = topic, Params = topicInfo });
            topic.UpdateValue("Waiting...");  //Excel2016不接受return返回的初始值。
            return ("Waiting...");            //WPS通过return获取初始值。
        }

        protected override void DisconnectData(Topic topic)
        {
            Log.Info($"RTD DisconnectData {topic.TopicId}");
            topics.Remove(topic.TopicId.ToString());
            if (topics.Count == 0) Stop();
        }
    }
}

